// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose:  Implementation of CXFormsElementRepeat class.
//   $Id$


#include "stdafx.h"
#include "RepeatUI.h"
#include "parseRepeat.h"
#include "repeat.h"
#include "XFormsApp.h"
#include "XFormsManager.h"
#include "groupUI.h"
#include <LibXPort/SmartPointer.h>
#include "FP_Definitions.h"
#include <HTMLDOMTranslator.h>
#include <Render.h>
#include <Render_i.c>
/*
#include "upload.h"
#include "formcontrol.h"
#include "Afxdlgs.h"
*/

extern CXFormsApp theApp;

CXFormsElementRepeat::CXFormsElementRepeat()
	:m_lIterations(0)
	//:m_pCurrentRep(NULL)
	, m_bRendered(false)
	,m_bDisplayAsColumns(false)
{
	m_guidEvents = DIID_HTMLInputTextElementEvents;
}

CXFormsElementRepeat::~CXFormsElementRepeat()
{
	try
	{
		if(m_pFormControl)
			put_iterations(0);
	}
	catch(...)
	{
		ASSERT(0);
	}
}

STDMETHODIMP CXFormsElementRepeat::Detach()
{
	if(m_pFormControl)
		put_iterations(0);
	return __super::Detach();
}

STDMETHODIMP CXFormsElementRepeat::SerialiseViewlink(IXMLDOMDocument *pSerialisationDOM, IXMLDOMNode *pThis)
{
	HRESULT hr = S_OK;
/*
	if(pSerialisationDOM && pThis && m_pControl) {
		CComPtr<IHTMLDocument> pViewlink;
		GetViewlinkDoc(&pViewlink);
		CComQIPtr<IHTMLDocument2> pViewlink2 = pViewlink;
		if(pViewlink2) {
			CComPtr<IHTMLElement> pViewlinkBody;
			pViewlink2->get_body(&pViewlinkBody);
			if(pViewlinkBody) {
				LibXPort::CSmartPointer<LibMSHTML::CXMLSerialisationVisitor> pVisitor = new LibMSHTML::CXMLSerialisationVisitor;
				pVisitor->SetDOM(pSerialisationDOM);
				LibXPort::CSmartPointer<LibMSHTML::CNode> pNode = new LibMSHTML::CElement(pViewlinkBody);
				LibMSHTML::CDocumentIterator i = pNode;
				++i;
				long nDepth = 0;
				CComQIPtr<IXMLDOMNode> pLastNode = pThis;
				while(i.IsValid()) {
					pNode = *i;
					if(pNode) {
						pNode->Accept(pVisitor);
						if(i.LastAction() == LibDOM2::CDOMIterator::TO_FIRST_CHILD) {
							++nDepth;
							CComPtr<IXMLDOMNode> p;
							if(pLastNode)
								pLastNode->appendChild(pVisitor->Serialisation(), &p);
						} else if(i.LastAction() == LibDOM2::CDOMIterator::TO_NEXT_SIBLING) {
							nDepth += i.LastActionDepthDifference();
							if(nDepth < 0)
								break;
							CComPtr<IXMLDOMNode> pParentNode;
							for(long j = 0; j >= i.LastActionDepthDifference(); --j) {
								pParentNode.Release();
								if(pLastNode) {
									pLastNode->get_parentNode(&pParentNode);
									pLastNode = pParentNode;
								}
							}
							if(pParentNode) {
								CComPtr<IXMLDOMNode> p;
								pParentNode->appendChild(pVisitor->Serialisation(), &p);
							}
						} else {
							CComPtr<IXMLDOMNode> p;
							pThis->appendChild(pVisitor->Serialisation(), &p);
						}
						if(pVisitor->Serialisation())
							pLastNode = pVisitor->Serialisation();
					}
					++i;
				}
			}
		}
	} else
		hr = E_POINTER;
*/
	return hr;
}

HRESULT CXFormsElementRepeat::attachEvents( IHTMLDocument3 * )
{
	HRESULT hr = S_OK;

	/*
	 * Cast the control that is handling the UI for us
	 */

	m_guidEvents = DIID_HTMLInputTextElementEvents;
	if(m_pEventSink)
	{
		CComQIPtr<IHTMLInputElement> pControl = m_pControl;
		if(pControl)
			hr = AtlAdvise(pControl, (IDispatch*) m_pEventSink, m_guidEvents, &m_dwCookie);
	}

	return hr;
}

CImpl* CXFormsElementRepeat::CreateImpl()
{
	CImpl* pFormControl = (CImpl*) new CImplRepeat(this);

	return pFormControl;
}



CXFormsParse* CXFormsElementRepeat::CreateParse(CImpl* pImpl)
{
	CXFormsParse* pParse = new CXFormsParseRepeat((CImplRepeat*) pImpl);

	return pParse;
}

/*
 * There are two ways we could do this:
 *
 * 1. Put up as many controls as are required to match the number attribute, even if there
 *		are less items in the collection than there are controls
 * 2. Put up a control for every element in the collection, but hide those controls that are
 *		'out of view'
 *
 * It's not really clear which is best, but the first feels right (for now!).
 */

HRESULT CXFormsElementRepeat::getUI(CComBSTR &sHtml)
{
	HRESULT hr = E_PENDING;
	if(m_pFormControl)
	{
		if(m_pFormControl->m_sAppearance == L"grid-left")
		{
	//		m_pFormControl->m_sAppearance = L"compact";
			m_bDisplayAsColumns = true;
		}

		if(m_bDisplayAsColumns || m_pFormControl->m_sAppearance == L"compact")
		{
			//load the content as xml to allow easier access to 
			//	rip out the labels and form the table rows.
			CDOM3Document doc;
			//wrap the content in <x /> to avoid trying to load with multiple 
			//	top level elements.

			VARIANT_BOOL b;
			doc.m_pXMLParser->loadXML(L"<x/>",&b);
			CComPtr<IXMLDOMElement> pDocumentElement;
			
			doc.m_pXMLParser->get_documentElement(&pDocumentElement);
			CHTMLDOMTranslator translator(CLSID_DOMDocument60);
			CComPtr<IHTMLElement> pMaster;
			GetMasterElement(&pMaster);
			CComQIPtr<IHTMLDOMNode> pMasterAsNode(pMaster);
			if(pMasterAsNode)
			{
				CComPtr<IHTMLDOMNode> pNode;
				pMasterAsNode->get_firstChild(&pNode);
				while(pNode)
				{
					CComPtr<IXMLDOMNode> pXMLNode;
					hr = translator.cloneNode(pNode,&pXMLNode,true);		
					if(SUCCEEDED(hr))
						pDocumentElement->appendChild(pXMLNode,0);
					
					CComPtr<IHTMLDOMNode> pNextNode;
					pNode->get_nextSibling(&pNextNode);
					pNode = pNextNode;
				}
				
			}
			if(SUCCEEDED(hr))
			{
				CComQIPtr<IXMLDOMDocument2> pDoc2 = doc.m_pXMLParser;
				if(pDoc2)
				{
					const CComBSTR bsSelectionNamespaces(L"SelectionNamespaces");
					CStringW sNSDecl;
					sNSDecl.Format(L"xmlns:xf=\"%s\"",theApp.m_sXFormsURI);
					CComBSTR bsNSDecl;
					bsNSDecl.Attach(sNSDecl.AllocSysString());
					hr = pDoc2->setProperty(bsSelectionNamespaces,CComVariant(bsNSDecl));
					if(SUCCEEDED(hr))
					{
						const CComBSTR sFindChildren(L"/x/*");
						pDoc2->selectNodes(sFindChildren,&m_pTRowContent);
						
						if(m_bDisplayAsColumns)
						{
							hr = PrepareColumnContent(sHtml);
						}
						else
						{
							hr = PrepareRowContent(sHtml);
						}
					}
				}
			}
			else
			{
				if(false)
				{
					CComPtr<IXMLDOMParseError> pE;
					hr = doc.m_pXMLParser->get_parseError(&pE);
					CComBSTR s;
					pE->get_reason(&s);
					CComBSTR s2;
					pE->get_srcText(&s2);
					::MessageBoxW(0,s,s2,0);
				}

			}
		}
	}
	return hr;
}

HRESULT CXFormsElementRepeat::PrepareColumnContent(CComBSTR & sHtml)
{
	HRESULT hr = S_OK;
	if(m_pTRowContent)
	{
		CComBSTR sTHeadContent;
		CComBSTR sFindLabelChild(L"xf:label");
		CComPtr<IXMLDOMNode> pNode;
		m_pTRowContent->nextNode(&pNode);
		int i = 0;
		while(pNode)
		{
			//extract the label child of that element.
			CComPtr<IXMLDOMNode> pLabelChild;
			pNode->selectSingleNode(sFindLabelChild,&pLabelChild);
			if(pLabelChild)
			{
				CComPtr<IXMLDOMNode> pRefAttr;
				pLabelChild->selectSingleNode(L"@ref",&pRefAttr);
				if(pRefAttr)
				{
					CStringW sTD;
					sTD.Format(L"<tr><td class=\"repeat-grid-header-cell\" id=\"row%i\"></td></tr>",i);
					sTHeadContent += sTD;
					pNode->removeChild(pLabelChild,0);
					//TODO: find out how I made this work for row!
				}
				else
				{
					//if a label exists, stick it in the THead row.
					CComBSTR sLabel;
					pLabelChild->get_xml(&sLabel);
					CStringW sTD;
					sTD.Format(L"<tr><td class=\"repeat-grid-header-cell\" id=\"row%i\">%s</td></tr>",i,sLabel);
					sTHeadContent += sTD;
					pNode->removeChild(pLabelChild,0);
				}
			} else if(!IsAction(pNode)) {
				//if there was no label, add an empty cell to the THead row
				CStringW sTD;
				sTD.Format(L"<tr><td class=\"repeat-grid-header-cell\" id=\"row%i\"></td></tr>",i);
				sTHeadContent += sTD;
			}
			//Set up for next iteration
			pNode.Release();
			m_pTRowContent->nextNode(&pNode);
			++i;
		}

		// Generate the table in its original (no iterations) form.
		CStringW sGridTable;
		sGridTable.Format(L"<style>.input-value{width:100%};</style><span id='control'><table id='controlTable' class=\"value repeat-value\" cellspacing=\"0\"><col span='1' class='repeat-grid-header'>%s</table></span>",sTHeadContent);
		sHtml.Attach(sGridTable.AllocSysString());
	}
	return hr;
}

HRESULT CXFormsElementRepeat::PrepareRowContent(CComBSTR & sHtml)
{
	HRESULT hr = S_OK;
	if(m_pTRowContent)
	{
		CComBSTR sTHeadContent;
		
		const CComBSTR sFindLabelChild(L"xf:label");
		CComPtr<IXMLDOMNode> pNode;
		m_pTRowContent->nextNode(&pNode);
		int i = 0;
		const wchar_t * sEmptyHeadTemplate = L"<th class=\"repeat-grid-header-cell column-%i\"></th>";
		const wchar_t * sLabelPopulatedHeadTemplate = L"<th class=\"repeat-grid-header-cell column-%i\">%s</th>";
		while(pNode)
		{
			//extract the label child of that element.
			CComPtr<IXMLDOMNode> pLabelChild;
			pNode->selectSingleNode(sFindLabelChild,&pLabelChild);
			if(pLabelChild)
			{
				CComPtr<IXMLDOMNode> pRefAttr;
				const CComBSTR sRef(L"@ref");
				pLabelChild->selectSingleNode(sRef,&pRefAttr);
				if(pRefAttr)
				{
					CStringW sTD;
					sTD.Format(sEmptyHeadTemplate,i);
					sTHeadContent += sTD;
				}
				else
				{
					//if a label exists, stick it in the THead row.
					CComBSTR sLabel;
					pLabelChild->get_xml(&sLabel);
					
					CStringW sTD;
					sTD.Format(sLabelPopulatedHeadTemplate,i,sLabel);
					sTHeadContent += sTD;
					pNode->removeChild(pLabelChild,0);
				}
			} else if(!IsAction(pNode)) {
				//if there was no label, add an empty cell to the THead row
				CStringW sTD;
				sTD.Format(sEmptyHeadTemplate,i);
				sTHeadContent += sTD;
			}
			//Set up for next iteration
			pNode.Release();
			m_pTRowContent->nextNode(&pNode);
			++i;
		}
		// Generate the table in its original (no iterations) form.
		CStringW sGridTable;
		const wchar_t * sGridTableTemplate = L"<style>.input-value{width:100%};</style><table id='control' class=\"value repeat-value\" cellspacing=\"0\"><thead class=\"repeat-grid-header\"><tr>%s</tr></thead></table>";
		sGridTable.Format(sGridTableTemplate,sTHeadContent);
		sHtml.Attach(sGridTable.AllocSysString());
	}
	return hr;
}

HRESULT CXFormsElementRepeat::formControlInitialize()
{
	return S_OK;
}

HRESULT CXFormsElementRepeat::InitBehaviour()
{
	HRESULT hr = __super::InitBehaviour();
	if(SUCCEEDED(hr) && m_pXForms)
	{
		CComPtr<IHTMLElement> pMaster;
		GetMasterElement(&pMaster);
		if(pMaster)
		{
			pMaster->setAttribute(CComBSTR(L"liveChildren"), CComVariant(L"yes"), 1);
			pMaster->setAttribute(CComBSTR(L"tabIndex"), CComVariant(-1, VT_I4), 1);
		}

		hr =  RegisterCustomEvents(sm_asEventNames,CXFORMSELEMENTREPEAT_EVENTS_LENGTH, m_alEventCookies);
	}
	return hr;
}

HRESULT CXFormsElementRepeat::GetFirstTableChild(IHTMLDOMNode * pParent, CComQIPtr<IHTMLTable> & pTable) const
{
	HRESULT hr = S_FALSE;
	CComPtr<IHTMLDOMNode> pTableNode;
	pParent->get_firstChild(&pTableNode);
	pTable = pTableNode;
	while(!pTable && pTableNode)
	{
		CComPtr<IHTMLDOMNode>pTableNode2;
		pTableNode->get_nextSibling(&pTableNode2);
		pTableNode = pTableNode2;
		pTable = pTableNode;
	}
	if(pTable)
		hr = S_OK;
	return hr;
}

HRESULT CXFormsElementRepeat::AddTableRowIterations(unsigned long lVal)
{
	HRESULT hr = S_FALSE;
	unsigned long l = m_lIterations;
	CComQIPtr<IHTMLDOMNode> pMaster = m_pControl;
	CComQIPtr<IHTMLTable> pTable;
	GetFirstTableChild(pMaster,pTable);
	if(pTable)
	{
		const CComBSTR sInsertLocation(L"beforeEnd");
		for(;l < lVal;++l)
		{
			CComPtr<IDispatch> pRowDisp;
			pTable->insertRow(-1,&pRowDisp);
			CComQIPtr<IHTMLElement2> pRowEl = pRowDisp;
			if(pRowEl)
			{ 
				CComQIPtr<IDispatch> pFactory;
				m_pXForms->GetElementFactory(&IID_IDispatch, reinterpret_cast<void**>(&pFactory));
				// Bug 983: Extra processing required to get the factory if we are in the renderer.
				if(!pFactory) {
					CComPtr<IHTMLDocument2> pDocument;
					m_pXForms->GetOwnerDocument(&pDocument);
					if(pDocument) {
						CComPtr<IHTMLWindow2> pWindow;
						pDocument->get_parentWindow(&pWindow);
						if(pWindow) {
							CComPtr<IDispatch> pExternal;
							pWindow->get_external(&pExternal);
							CComQIPtr<IRendererFactories> pFactories = pExternal;
							if(pFactories) {
								CComPtr<IUnknown> pUnknown;
								pFactories->get_XFormsFactory(&pUnknown);
								pFactory = pUnknown;
							}
						}
					}
				}
				CComVariant vtFac(pFactory);
				CComQIPtr<IHTMLElement> pRowEl0(pRowEl);
				if(pRowEl0)
				{
					CComBSTR sClassName = (l % 2)?L"repeat-item  repeat-item-even":L"repeat-item repeat-item-odd";
					pRowEl0->put_className(sClassName);
				}
				//Add the group behaviour to the table row, and store the cookie.
				long lCookie;
				pRowEl->addBehavior(g_lpwszTagGroup,&vtFac,&lCookie);
				const CComBSTR sAttachmentCookieAttributeName(L"fpBehaviourAttachmentCookie");
				pRowEl0->setAttribute(sAttachmentCookieAttributeName,CComVariant(lCookie),1);
				
				CComQIPtr<IHTMLTableRow> pRow = pRowDisp;
				CComPtr<IXMLDOMNode> pN;
				m_pTRowContent->reset();
				m_pTRowContent->nextNode(&pN);
				int i = 0;
				CStringW s;
 				while(pN)
				{
					// Bug 1018: Do not add actions to the grid repeat UI (they cause extraneous columns).
					if(!IsAction(pN)) {
						//Insert a new cell
						CComPtr<IDispatch> pCellDisp;
						pRow->insertCell(-1,&pCellDisp);
						CComQIPtr<IHTMLElement> pCellEl(pCellDisp);
						if(pCellEl)
						{
							//mark up the new cell as a grid cell
							s.Format(L"repeat-grid-cell column-%i",i);
							CComBSTR bs;
							bs.Attach(s.AllocSysString());
							pCellEl->put_className(bs);
							//bung in the XForms control that corresponds to this cell
							CComBSTR bsCellContent;
							pN->get_xml(&bsCellContent);
							pCellEl->insertAdjacentHTML(sInsertLocation,bsCellContent);
							++i;
						}
					}
					pN.Release();
					m_pTRowContent->nextNode(&pN);
				}
				++m_lIterations;
			}
		}
	}
	return hr;
}

//#define REPEAT_ITERATION_CONTENT_INSERTION_WITH_MSGPUMP 1

#include "WaitWithMessageLoop.h"
HANDLE g_hMutex_repeat_iterations = 0;

HRESULT InsertIterationContent(CXFormsElementRepeat*pThis,CComQIPtr<IHTMLElement> pIteration,const CComBSTR& sInsertLocation,const CComBSTR& sContent)
{
#ifdef REPEAT_ITERATION_CONTENT_INSERTION_WITH_MSGPUMP

	MSG msg;
	// force msg-q to be created just in case, NOP otherwise
//	PeekMessage(&msg, NULL, WM_APP, WM_APP, PM_NOREMOVE | PM_NOYIELD);
	// msg to myself to do work
	LPARAM p = (LPARAM)pThis;
	PostThreadMessage(GetCurrentThreadId(), WM_APP+198, 0,p);
	// msg-pump
	while (GetMessage(&msg, NULL, 0, 0))
	{
		// this was my message -- time to do my work
		if (msg.hwnd == NULL && msg.message == WM_APP+198 && msg.lParam == p)
		{
#endif
			pIteration->insertAdjacentHTML(sInsertLocation,sContent);
#ifdef REPEAT_ITERATION_CONTENT_INSERTION_WITH_MSGPUMP
			PostThreadMessage(GetCurrentThreadId(), WM_APP+199, 0, p);
		}
		else if (msg.hwnd == NULL && msg.message == WM_APP+199 && msg.lParam == p)
		{
			break;
		}/*
		else if (msg.hwnd == NULL && (msg.message == WM_APP+198 || msg.message == WM_APP+199 ))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			break;
		}*/
		else if(msg.message == WM_TIMER)
		{
			break;
		}
		else
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
#endif

	return S_OK;
}

HRESULT CXFormsElementRepeat::AddNormalIterations(unsigned long lVal)
{
	HRESULT hr = S_FALSE;
	const CComBSTR sInsertLocation(L"beforeEnd");
	BSTR sItem =m_pXForms->GetRepeatIterationWrapper();
	

#ifdef REPEAT_ITERATION_CONTENT_INSERTION_WITH_MSGPUMP

	MSG msg;
	// force msg-q to be created just in case, NOP otherwise
	PeekMessage(&msg, NULL, WM_APP, WM_APP, PM_NOREMOVE | PM_NOYIELD);
	// msg to myself to do work
	LPARAM p = (LPARAM)this;
	PostThreadMessage(GetCurrentThreadId(), WM_APP+197, 0,p);
	// msg-pump
	while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
	{
		// this was my message -- time to do my work
		if (msg.hwnd == NULL && msg.message == WM_APP+197 && msg.lParam == p)
		{
#endif
			if(!g_hMutex_repeat_iterations)
			{
				g_hMutex_repeat_iterations = CreateSemaphore(0, 1,1, _T("XPORT_FP_mutex_repeat_iteration"));
			}
			else
			{
				//WaitForSingleObject(g_hMutex_repeat_iterations,INFINITE);
				xport::WaitWithMessageLoop(g_hMutex_repeat_iterations);
			}
			//We cannot build a long string of items before insertion
			//	as this causes them to initialise in reverse order.
			unsigned long l = m_lIterations;
			if(0 == m_lIterations)
			{
				m_pControl->put_innerHTML(sItem);
				l = 1;
			}
		//TODO: stick this in a message loop and see if building up the big string will now work

			for(;l < lVal;++l)
			{
				m_pControl->insertAdjacentHTML(sInsertLocation,sItem);
			}
			::SysFreeString(sItem);
			LONG pc;
			::ReleaseSemaphore(g_hMutex_repeat_iterations,1,&pc);
		
#ifdef REPEAT_ITERATION_CONTENT_INSERTION_WITH_MSGPUMP
			PostThreadMessage(GetCurrentThreadId(), WM_APP+196, 0,p);
		}
		else if(msg.message == WM_APP+196 && msg.lParam == p)
		{
			break;
		}
		else if(msg.message == WM_TIMER)
		{
			//break;
		}
		else
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
#endif
	//Get the new iterations, and insert the contents.
	//	This could not have been done before insertion
	//	as it would have resulted in the children of
	//	repeat-item being initialised before the repeat
	//	item, thereby having incorrect context.

	CComPtr<IDispatch> pDisp;
	m_pControl->get_children(&pDisp);
	CComQIPtr<IHTMLElementCollection> pColl = pDisp;
	if(pColl)
	{
		//TODO: probably could be done  using static_cast.
		CImplRepeat * pRepeat = dynamic_cast<CImplRepeat*>(m_pImpl);
		if(pRepeat)
			{
			const CComBSTR sOddClassName(L"repeat-item repeat-item-odd");
			const CComBSTR sEvenClassName(L"repeat-item  repeat-item-even");;
		
			//loop through all the new iterations.
			unsigned long lFilledIterations = m_lIterations;
			m_lIterations = lVal;
			for(;lFilledIterations < lVal;++lFilledIterations)
			{
				CComPtr<IDispatch> pDispEl;
				pColl->item(CComVariant(lFilledIterations),CComVariant(0),&pDispEl);
				CComQIPtr<IHTMLElement> pIteration(pDispEl);
				if(pIteration)
				{
					pIteration->put_className((lFilledIterations % 2)?sEvenClassName:sOddClassName);
					InsertIterationContent(this, pIteration,sInsertLocation,pRepeat->m_sContent);
				}
				else
				{
					//Something is wrong!
					//I don't strictly know if this is an error,  I don't  
					//	even know if this point can be reached,
					//	so I am just going to record it and stop the loop.
					//	An error should be spotted later if this occurs
					// (in CImplRepeat::buildDependecies())
					AtlTrace("\n'formsplayer.dll': more iterations were expected than encountered\n");
					break;
				}
			}
		}
	}

	return hr;
}
HRESULT CleanupNode(IHTMLDOMNode * pEl)
{
	HRESULT hr = S_OK;
	CComPtr<IDispatch> pDisp;
	pEl->get_childNodes(&pDisp);

	CComQIPtr<IHTMLDOMChildrenCollection> pColl = pDisp;
	if(pColl)
	{
		CComPtr<IUnknown> punkEnum;	
		pColl->get__newEnum(&punkEnum);
		CComQIPtr<IEnumVARIANT> pEnum(punkEnum);
		if(pEnum)
		{
			pEnum->Reset();
			CComVariant vtChildEl;
			unsigned long lLeft;
			HRESULT _hr = pEnum->Next(1,&vtChildEl,&lLeft);
			
			while(_hr == S_OK)
			{
				CComQIPtr<IHTMLDOMNode> pChildNode;
				if(vtChildEl.vt == VT_UNKNOWN)
					pChildNode= vtChildEl.punkVal;
				else if(vtChildEl.vt == VT_DISPATCH)
					pChildNode= vtChildEl.pdispVal;
				if(pChildNode)
				{
					CComPtr<IHTMLDOMNode> pNodeOut;
					pEl->removeChild(pChildNode,&pNodeOut);
					CleanupNode(pChildNode);
				}
				vtChildEl.Clear();
				_hr = pEnum->Next(1,&vtChildEl,&lLeft);
			}
		}
	}
	return hr;
}

HRESULT CXFormsElementRepeat::RemoveTableRowIterations(unsigned long lVal)
{
	HRESULT hr = S_FALSE;
	CComQIPtr<IHTMLDOMNode> pMaster = m_pControl;
	CComPtr<IHTMLDOMNode> pTableNode;
	CComQIPtr<IHTMLTable> pTable;
	GetFirstTableChild(pMaster,pTable);
	if(pTable)
	{
		CComPtr<IHTMLElementCollection> pColl;
		pTable->get_rows(&pColl);
		if(pColl)
		{
			long l;
			pColl->get_length(&l);
			CComVariant vt0(0l);
			while(m_lIterations > lVal)
			{
				CComPtr<IDispatch> pDisp;
				pColl->item(CComVariant(l-1),vt0,&pDisp);

				CComQIPtr<IHTMLElement> pEl(pDisp);
				if(pEl)
				{
					CComVariant vtCookie;
					hr = pEl->getAttribute(L"fpBehaviourAttachmentCookie",1,&vtCookie);
					if(S_OK == hr)
					{
						CComQIPtr<IHTMLElement2> pEl2(pEl);
						VARIANT_BOOL b;
						pEl2->removeBehavior(vtCookie.lVal,&b);
					}
					CComQIPtr<IHTMLTableRow> pRow(pEl);
					if(pRow)
					{
						CComPtr<IHTMLElementCollection> pCells;
						pRow->get_cells(&pCells);
						if(pCells)
						{
							CComPtr<IUnknown> punkEnum;	
							pCells->get__newEnum(&punkEnum);
							CComQIPtr<IEnumVARIANT> pEnum(punkEnum);
							if(pEnum)
							{
								pEnum->Reset();
								CComVariant vtCell;
								unsigned long lLeft;
								HRESULT _hr = pEnum->Next(1,&vtCell,&lLeft);
								
								while(_hr == S_OK)
								{
									CComQIPtr<IHTMLElement> pElCell;
									if(vtCell.vt == VT_UNKNOWN)
										pElCell= vtCell.punkVal;
									else if(vtCell.vt == VT_DISPATCH)
										pElCell= vtCell.pdispVal;
									if(pElCell)
									{
										CComQIPtr<IHTMLDOMNode> pNode(pElCell);
										CleanupNode(pNode);
									}
									vtCell.Clear();
									_hr = pEnum->Next(1,&vtCell,&lLeft);
								}
							}
						}
					}
					pTable->deleteRow(-1);
					--m_lIterations;
					--l;
				}	
			}
		}
	}
	return hr;
}

STDMETHODIMP CXFormsElementRepeat::GetProperty(BSTR sProperty, BSTR * sValue)
{
	long lIndex = 0;
	wcstol(sProperty,0,lIndex);
	if(lIndex)
	{
		
	}
	return __super::GetProperty(sProperty, sValue);
}

#include "MSHTMLDomPurger.h"
HRESULT CXFormsElementRepeat::RemoveNormalIterations(unsigned long lVal)
{
	HRESULT hr = E_FAIL;
	CComPtr<IDispatch> pDisp;
	CComQIPtr<IHTMLDOMNode> pControl = m_pControl;
	if(pControl)
	{
		pControl->get_childNodes(&pDisp);
		CComQIPtr<IHTMLDOMChildrenCollection> pColl = pDisp;
		if(pColl) 
		{
			while(m_lIterations > lVal)
			{
				CComPtr<IDispatch> pDispEl;
				hr = pColl->item(m_lIterations-1,&pDispEl);
				if(SUCCEEDED(hr) && pDispEl)
				{
					m_lIterations--;
					CComQIPtr<IHTMLDOMNode> pNode = pDispEl;
					if(pNode)
					{
						//now that purger has been added - investigate whether the DOM methods 
						//	warned about below work.
						CMSHTMLDOMPurger * pPurger = CMSHTMLDOMPurger::GetPurger();
						if(pPurger)
							pPurger->Purge(pNode);
						//WARNING: Trying to use either of these two sensible-looking
						//	DOM Methods will result in memory leaking out of your ears.
						//	Behaviours attacherd to the nodes are not detached!
						//CComPtr<IHTMLDOMNode> pNodeOut;
						//pControl->removeChild(pNode, &pNodeOut);
						//pNode->removeNode(VARIANT_TRUE, &pNodeOut);
						// HACK: This fix for the above warning causes strange repeat behaviour for Icon.
						//CComQIPtr<IHTMLElement> pEl = pNode;
						//if(pEl)
						//	pEl->put_outerHTML(CComBSTR(L""));
						// HACK: Reinstating the original DOM method.
						CComPtr<IHTMLDOMNode> pNodeOut;
						pNode->removeNode(VARIANT_TRUE, &pNodeOut);
					}
				}
				else
				{
					::AtlTrace("w: formSplayer.dll: Repeat attempting to remove apparently fictional iteration.\n");
					ASSERT(0);
					break;
				}
			}
		}
	}

	return hr;
}

HRESULT CXFormsElementRepeat::RemoveColumnIterations(unsigned long lVal)
{
	HRESULT hr = RemoveNormalIterations(lVal);
	CComQIPtr<IHTMLTable> pTable;
	CComVariant vt0(0l);

	CComQIPtr<IHTMLElement2> pControl2 = m_pControl;
	if(pControl2)
	{
		CComPtr<IHTMLElementCollection> pColl;
		pControl2->getElementsByTagName(L"table",&pColl);
		if(pColl)
		{
			CComPtr<IDispatch> pDisp;
			pColl->item(vt0,vt0,&pDisp);
			pTable = pDisp;
		}
	}


	if(pTable)
	{
		CComPtr<IHTMLElementCollection> pColl;
		pTable->get_rows(&pColl);
		if(pColl)
		{
			long lLen;
			pColl->get_length(&lLen);
			for(unsigned long i = m_lIterations;i >= lVal;--i)
			{
				long l;
				for(l = 0; l < lLen; l++)
				{
					CComPtr<IDispatch> pDisp;
					pColl->item(CComVariant(l),vt0,&pDisp);
					CComQIPtr<IHTMLTableRow> pRow(pDisp);
					if(pRow)
					{
						//have to blank the cell first, otherwise contained behaviours aren't detached
						//	Stupid fucking IE!!

						CComPtr<IHTMLElementCollection> pColl;
						pRow->get_cells(&pColl);
						long len;
						pColl->get_length(&len);
						CComPtr<IDispatch> pDispCell;
						pColl->item(CComVariant(len-1),vt0,&pDispCell);
						CComQIPtr<IHTMLDOMNode> pCell(pDispCell);
						CleanupNode(pCell);
						//Then it can be deleted.
						pRow->deleteCell(-1);
					}				
				}
			}

			CComPtr<IDispatch> pDisp;
			m_pControl->get_children(&pDisp);
			CComQIPtr<IHTMLElementCollection> pCollChildren = pDisp;
			if(pCollChildren)
			{
				CComVariant vt0(0l);
				//loop through all the new iterations.
				for(;m_lIterations > lVal;--m_lIterations)
				{
					CComPtr<IDispatch> pDispEl;
					pCollChildren->item(CComVariant(L"group"),CComVariant(m_lIterations),&pDispEl);
					CComQIPtr<IHTMLDOMNode> pIteration = pDispEl;
						CleanupNode(pIteration);
				}
			}
		}
	}

	return hr;
}

HRESULT CXFormsElementRepeat::AddColumnIterations(unsigned long lVal)
{
	//This differs from AddNormalIterations in that no content is added to the group elements.
	HRESULT hr = S_FALSE;
	CComBSTR sInsertLocation(L"beforeEnd");
	BSTR sItem =m_pXForms->GetRepeatIterationWrapper();
	
	//We cannot build a long string of items before insertion
	//	as this causes them to initialise in reverse order.
	unsigned long l = m_lIterations;
	for(;l < lVal;++l)
	{
		m_pControl->insertAdjacentHTML(sInsertLocation,sItem);
	}
	::SysFreeString(sItem);
	CComPtr<IDispatch> pDisp;
	m_pControl->get_children(&pDisp);
	CComQIPtr<IHTMLElementCollection> pColl = pDisp;
	if(pColl)
	{
		CComVariant vt0(0l);
		//loop through all the new iterations.
		CComBSTR sDisplayNone(L"none");
		for(;m_lIterations < lVal;++m_lIterations)
		{
			CComPtr<IDispatch> pDispEl;
			pColl->item(CComVariant(m_lIterations+1),CComVariant(0),&pDispEl);
			CComQIPtr<IHTMLElement> pIteration = pDispEl;
			if(pIteration)
			{
				CComPtr<IHTMLStyle> pStyle;
				pIteration->get_style(&pStyle);
				if(pStyle)
					pStyle->put_display(sDisplayNone);
				CComQIPtr<IHTMLTable> pTable;
				CComQIPtr<IHTMLElement2> pControl2 = m_pControl;
				if(pControl2)
				{
					CComPtr<IHTMLElementCollection> pColl;
					pControl2->getElementsByTagName(L"table",&pColl);
					CComPtr<IDispatch> pDisp;
					pColl->item(vt0,vt0,&pDisp);
					pTable = pDisp;
				}

				if(pTable)
				{
					CComPtr<IHTMLElementCollection> pColl;
					pTable->get_rows(&pColl);
					if(pColl)
					{
						long lLen;
						pColl->get_length(&lLen);
						
						m_pTRowContent->reset();
						long l;
						for(l = 0; l < lLen; l++)
						{
							CComPtr<IXMLDOMNode> pNode;
							m_pTRowContent->nextNode(&pNode);
							if(pNode)
							{
								CComPtr<IDispatch> pDisp;
								pColl->item(CComVariant(l),vt0,&pDisp); 
								CComQIPtr<IHTMLTableRow> pRow = pDisp;
								if(pRow)
								{
									CComPtr<IDispatch> pDispCell;
									pRow->insertCell(-1,&pDispCell);
									CComQIPtr<IHTMLElement> pCell(pDispCell);
									if(pCell)
									{
										pCell->setAttribute(L"parentOrdinal",CComVariant(m_lIterations),1);
										CComBSTR bsCellContent;
										pNode->get_xml(&bsCellContent);
										pCell->put_innerHTML(bsCellContent);

										CComBSTR sClassName = (m_lIterations % 2)?L"repeat-item  repeat-item-even":L"repeat-item repeat-item-odd";
										pCell->put_className(sClassName);

										CComPtr<IElementBehavior> pBehavior;
										CBinaryBehavior::GetNamedBehavior(g_bsAttrXFormsElementController,pIteration,&pBehavior);
										if(pBehavior)
										{
											CXFormsElementGroup * pGroup = dynamic_cast<CXFormsElementGroup *>(pBehavior.p);
											if(pGroup)
												pGroup->AddOutOfLineElement(pCell);
										}
									}
								}
							}

						}
					}
				}
			}
			else
			{
				//Something is wrong!
				//I don't strictly know if this is an error,  I don't  
				//	even know if this point can be reached,
				//	so I am just going to record it and stop the loop.
				//	An error should be spotted later if this occurs
				// (in CImplRepeat::buildDependecies())
				AtlTrace("\n'formsplayer.dll': more iterations were expected than encountered\n");
				break;
			}

		}
	}

	return hr;
}

HRESULT CXFormsElementRepeat::put_iterations(unsigned long lVal)
{
	//puts the actual current iteration count to the UI.
	//if the new iteration count is higher, create iterations 
	//	and insert, if lower, delete some.
	HRESULT hr = E_FAIL;
	// Bug 577 - the render() method must be called before put_iterations()
	// as it populates the m_pControl property.
	if(!m_pControl)
	{
		if(!m_bRendered)
			render();
		GetMasterElement(&m_pControl);
		
		if(m_pControl)
		{
			hr = S_OK;
			if(lVal > m_lIterations)
			{
				
				
				if(m_bDisplayAsColumns)
				{
					AddColumnIterations(lVal);
				}
				else if(m_pTRowContent)
				{
					AddTableRowIterations(lVal);
				}
				else
				{
					AddNormalIterations(lVal);
				}
				
			}
			else if(m_lIterations > lVal || lVal == 0)
			{
				if(m_bDisplayAsColumns)
				{
					RemoveColumnIterations(lVal);
				}
				else if(m_pTRowContent)
				{
					RemoveTableRowIterations(lVal);
				}
				else 
				{
					RemoveNormalIterations(lVal);
				}
				m_lIterations = lVal;
			}
		}
		m_pControl.Release();
	}

	return hr;
}
unsigned long CXFormsElementRepeat::smRepeatsProcessingElementReady = 0;
HRESULT CXFormsElementRepeat::ElementReady(void)
{
	++smRepeatsProcessingElementReady;
	HRESULT hr = __super::ElementReady();
	if(m_pImpl && (SUCCEEDED(hr) || hr == E_ABORT)) 
	{
		//BUG 699 and BUG 700: The fix to bug 110 caused problems with nested repeats.
		if(smRepeatsProcessingElementReady == 1)
		{
			int nRepeats;
			//This inner test may be redundant.
			if(m_pXForms && m_pXForms->IsRepeatBeingWaitedFor(m_pImpl->m_sId, nRepeats, true) && nRepeats == 0) {
				// Bug 110: index() not working from xf:bind attributes
				m_pXForms->rebuild(true);
			}
		}
	}
	--smRepeatsProcessingElementReady;

	return hr;
}

HRESULT CXFormsElementRepeat::render(void)
{
	HRESULT hr = S_OK;
	if(!m_bRendered)
	{
		CComBSTR sHTML;
		getUI(sHTML);
		if(sHTML)
		{
			CComPtr<IHTMLElement> pEl;
			GetMasterElement(&pEl);
			hr = pEl->put_innerHTML(sHTML);
		}
		m_bRendered = true;
	}
	return hr;
}

// Bug 1021 - The repeat shouldn't reflect the state of its node bindings 
// that is the responsibility of the repeat iterations. So these methods do 
// nothing
HRESULT CXFormsElementRepeat::SetClassProperty(bool /*bEnabled*/, bool /*bReadOnly*/, bool /*bValid*/, bool /*bRequired*/) {
	return S_OK;
}

// Bug 1021 - The repeat shouldn't reflect the state of its node bindings 
// that is the responsibility of the repeat iterations. So these methods do 
// nothing
HRESULT CXFormsElementRepeat::required(BOOL ) {
	return S_OK;
}

// Bug 1021 - The repeat shouldn't reflect the state of its node bindings 
// that is the responsibility of the repeat iterations. So these methods do 
// nothing
HRESULT CXFormsElementRepeat::enable(BOOL ){
	return S_OK;
}

// Bug 1021 - The repeat shouldn't reflect the state of its node bindings 
// that is the responsibility of the repeat iterations. So these methods do 
// nothing
HRESULT CXFormsElementRepeat::readonly(BOOL ) {
	return S_OK;
}

// Bug 1021 - The repeat shouldn't reflect the state of its node bindings 
// that is the responsibility of the repeat iterations. So these methods do 
// nothing
HRESULT CXFormsElementRepeat::valid(BOOL ) {
	return S_OK;
}

// Bug 1018: Do not add actions to the grid repeat UI (they cause extraneous columns).
bool CXFormsElementRepeat::IsAction(IXMLDOMNode *pNode)
{
	bool bIsAction = false;
	CComQIPtr<IXMLDOMElement> pElement = pNode;
	if(pElement) {
		CComBSTR sElementName;
		pElement->get_tagName(&sElementName);
		if(sElementName.Length() > 0 ) {
			wchar_t *sUnqualifiedName = wcsrchr(sElementName.m_str, L':');
			if(!sUnqualifiedName)
				sUnqualifiedName = sElementName.m_str;
			else
				sUnqualifiedName++;
			if(wcsicmp(sUnqualifiedName, L"action") == 0
			   || wcsicmp(sUnqualifiedName, L"dispatch") == 0
			   || wcsicmp(sUnqualifiedName, L"setfocus") == 0
			   || wcsicmp(sUnqualifiedName, L"load") == 0
			   || wcsicmp(sUnqualifiedName, L"setvalue") == 0
			   || wcsicmp(sUnqualifiedName, L"send") == 0
			   || wcsicmp(sUnqualifiedName, L"reset") == 0
			   || wcsicmp(sUnqualifiedName, L"message") == 0
			   || wcsicmp(sUnqualifiedName, L"insert") == 0
			   || wcsicmp(sUnqualifiedName, L"delete") == 0
			   || wcsicmp(sUnqualifiedName, L"setindex") == 0
			   || wcsicmp(sUnqualifiedName, L"duplicate") == 0
			   || wcsicmp(sUnqualifiedName, L"destroy") == 0
			   || wcsicmp(sUnqualifiedName, L"close") == 0
			   || wcsicmp(sUnqualifiedName, L"rebuild") == 0
			   || wcsicmp(sUnqualifiedName, L"recalculate") == 0
			   || wcsicmp(sUnqualifiedName, L"revalidate") == 0
			   || wcsicmp(sUnqualifiedName, L"refresh") == 0)
			{
				bIsAction = true;
			}
		}
	}
	return bIsAction;
}
